#ifndef _BTREE_MEM_POOL_H
#define _BTREE_MEM_POOL_H

#ifndef MB
#define MB	(1024 * 1024)
#endif

#include <libpmem.h>
#include <stdio.h>
#include <signal.h>
#include <stdlib.h>
#include <assert.h>
#include <unistd.h>
#include <fcntl.h>
#include <malloc.h>
#include <string.h>
#include <pthread.h>

class mempool {

private:
    long long mempool_align;
    long long mempool_size;
    char * mempool_start;
    char * mempool_cur;
    char * mempool_end;
    char * mempool_free_node;
    const char * mempool_name;

 public:

    mempool() { 
        mempool_start = mempool_cur = mempool_end = NULL;
        mempool_free_node = NULL;
    }
    ~mempool() {}

    void init(char *start, long long size, long long align, const char * name) {
        mempool_align = align;
        mempool_size = size;
        mempool_start = start;

        mempool_cur = mempool_start;
        mempool_end = mempool_start + size;
        mempool_free_node = NULL;

        mempool_name = name;
    }

    char * get_base() {
        return mempool_start;
    }

    void print_params(FILE *fp=stdout) {
        fprintf(fp, "%s\n", mempool_name);
        fprintf(fp, "mempool_align=%lld\n", mempool_align);
        fprintf(fp, "mempool_size=%lld\n", mempool_size);
        fprintf(fp, "mempool_start=%p\n", mempool_start);
        fprintf(fp, "mempool_cur=%p\n", mempool_cur);   //%p表示以16进制打印指针。
        fprintf(fp, "mempool_end=%p\n", mempool_end);
        fprintf(fp, "mempool_free_node=%p\n\n", mempool_free_node);
    }

    void print_usage() {
        long long  used = (mempool_cur - mempool_start);
        long long  ff = 0;
        for(char *p = mempool_free_node; p; p= *((char **)p))
            ff ++;

        printf("%s: total %.1lfMB, use %.1lfMB, among which %lld free nodes\n",
                mempool_name, ((double)mempool_size)/MB, ((double)used)/MB, ff);
    }

public:

    /**
    * 从内存池的末尾中分配一块内存，size指定大小，返回内存块的开始地址，确保size是对齐的。
    */
    void * alloc(unsigned long long size) {
        if(mempool_cur + size <= mempool_end) {
            register char *p;
            p = mempool_cur;
            mempool_cur += size;
            return (void *)p;
        }
        fprintf(stderr, "%s alloc - run out of memory!\n", mempool_name);
        exit(1);
    }

    /**
    * 释放内存，p是内存块的指针
    */
    void free(void *p) { }

    /**
    *  分配的size应该每次一样，且比 sizeof(void *) 大
    */
    void * alloc_node(int size) {
        if(mempool_free_node) {
            register char *p;
            p = mempool_free_node;
            mempool_free_node = *((char **)p);
            return (void *) p;
        } else {
            return alloc(size);
        }
    }

    void free_node(void *p) {
        *((char **)p) = mempool_free_node;
        mempool_free_node = (char *)p;
    }


    void print_free_nodes() {
        char *p = mempool_free_node;
        printf("%s free nodes:\n", mempool_name);
        while(p != NULL) {
            printf("%p -> ", p);
            p = *((char **)p);
        }
        printf("nil\n\n");
    }

};

class threadNVMPools {
public:
    mempool     *tm_pools;       /* pools[0..num_workers-1] */
    int          tm_num_workers;
    char *       tm_buf;         /* start address of allocated memory */
    long long    tm_size;        /* tm_buf size */
    const char * tn_nvm_file;
public:
    threadNVMPools() {
        tm_pools= NULL; 
        tm_num_workers= 0;
        tm_buf= NULL;   
        tm_size= 0;
        tn_nvm_file=NULL;
    }
    ~threadNVMPools();
    void init(int num_workers, const char * nvm_file, long long size=20*MB);
    void print(void);
    void print_usage(void);
}; 
extern thread_local int worker_id;  /* in Thread Local Storage */
extern threadNVMPools   the_thread_nvmpools;

#define the_nvmpool        (the_thread_nvmpools.tm_pools[worker_id])
#define nvmpool_alloc       the_nvmpool.alloc
#define nvmpool_free        the_nvmpool.free
#define nvmpool_alloc_node  the_nvmpool.alloc_node
#define nvmpool_free_node   the_nvmpool.free_node
#endif